package very.util.rpc

import com.github.tototoshi.play2.json4s.Json4s
import org.json4s.JValue
import play.api.mvc.{ Action, ActionFilter, ActionRefiner, AnyContent, ControllerComponents, Request, Result }
import sun.net.util.IPAddressUtil
import very.util.response.ResponseFormat

import scala.concurrent.{ ExecutionContext, Future }

// 简单一个 KEY 了事
trait RpcCtrHelper[CommonEntity[_] <: Request[_]] {
  protected def KEY: String
  protected def json4s: Json4s
  protected def controllerComponents: ControllerComponents

  protected def convertEntity[A](r: Request[A]): Either[Result, CommonEntity[A]]

  object RpcAuthActionRefiner extends ActionRefiner[Request, CommonEntity] {
    override protected def refine[A](request: Request[A]): Future[Either[Result, CommonEntity[A]]] = {
      Future.successful(convertEntity(request))
    }

    override protected def executionContext: ExecutionContext = controllerComponents.executionContext
  }

  object RpcAuthFilter extends ActionFilter[Request] {
    override protected def filter[A](request: Request[A]): Future[Option[Result]] = {
      val isInternalIp = RpcCtrHelper.isInternalIp(request.remoteAddress)
      val isCorrectKey = request.headers.get(Common.HEAD_KEY).contains(KEY)
      Future.successful((isInternalIp, isCorrectKey) match {
        case (true, true) => None
        case (false, _) =>
          Some(
            ResponseFormat
              .errorResponse("InValid Auth, Forbid")
              .withHeaders(play.api.http.HeaderNames.CONTENT_TYPE -> play.api.http.MimeTypes.JSON)
          )
        case _ =>
          Some(
            ResponseFormat
              .errorResponse("InValid Auth")
              .withHeaders(play.api.http.HeaderNames.CONTENT_TYPE -> play.api.http.MimeTypes.JSON)
          )
      })
    }

    override protected def executionContext: ExecutionContext = controllerComponents.executionContext
  }

  def rpcAct[A, B](fn: Request[JValue] => Either[A, B]): Action[JValue] =
    controllerComponents.actionBuilder.andThen(RpcAuthFilter)(json4s.json) { implicit req =>
      ResponseFormat.eitherToResponse(fn(req))
    }
  def rpcAuthAct[A, B](fn: CommonEntity[JValue] => Either[A, B]): Action[JValue] = {
    controllerComponents.actionBuilder.andThen(RpcAuthFilter).andThen(RpcAuthActionRefiner)(json4s.json) {
      implicit req =>
        ResponseFormat.eitherToResponse(fn(req))
    }
  }
  def rpcGAct[A, B](fn: Request[AnyContent] => Either[A, B]): Action[AnyContent] =
    controllerComponents.actionBuilder.andThen(RpcAuthFilter) { implicit req =>
      ResponseFormat.eitherToResponse(fn(req))
    }
  def rpcAuthGAct[A, B](fn: CommonEntity[AnyContent] => Either[A, B]): Action[AnyContent] = {
    controllerComponents.actionBuilder.andThen(RpcAuthFilter).andThen(RpcAuthActionRefiner) { implicit req =>
      ResponseFormat.eitherToResponse(fn(req))
    }
  }
}

object RpcCtrHelper {
  private val SECTION_1 = 0x0A.toByte
  private val SECTION_2 = 0xAC.toByte
  private val SECTION_3 = 0x10.toByte
  private val SECTION_4 = 0x1F.toByte

  private val SECTION_5 = 0xC0.toByte
  private val SECTION_6 = 0xA8.toByte

  protected def isInternalIp(ip: String): Boolean = {
    val addr = IPAddressUtil.textToNumericFormatV4(ip)
    addr(0) match {
      case SECTION_1 =>
        true
      case SECTION_2 if addr(1) >= SECTION_3 && addr(1) <= SECTION_4 =>
        true
      case SECTION_5 if addr(1) == SECTION_6 => true
      case _                                 => false
    }

  }
}
